# Eq, Order и PartialOrder

Эти классы типов предоставляют типобезопасные функции эквивалентности и сравнения.
Сравнение может быть полным (`Order`) или частичным (`PartialOrder`);
хотя неопределенные элементы, такие как `NaN` или `null`, вызовут проблемы в реализациях по умолчанию
(для чисел с плавающей запятой альтернативные реализации, которые учитывают `NaN`,
могут быть импортированы из `spire.optional.totalfloat.*`).

## Eq

Spire предоставляет класс типов `Eq[A]` для представления равенства типов. 
Это позволяет рассуждать о типах, для которых не существует полезного в вычислительном отношении понятия равенства, 
а также избегать ошибок программирования, вызванных универсальным равенством.

`Eq[A]` предоставляет два оператора:

- `eqv` (`===`): эквивалентность - проверка на равенство.
- `neqv` (`=!=`): различие - проверка на отсутствие равенства (по умолчанию `!(a === b)`).

Spire требует для `eqv` соблюдения законов отношения эквивалентности, а именно:

- `a === a` (_рефлексивность_)
- если `a === b`, тогда `b === a` (_симметрия_)
- если `a === b`, то `a` есть `b` (_антисимметрия_)
- если `a === b` и `b === c`, тогда `a === c` (_транзитивность_)

Свойство антисимметрии может показаться запутанным. 
Идея состоит в том, что если `a === b`, тогда `a` и `b` должны быть взаимозаменяемыми, 
так что для любого выражения `f(x)`, `f(a) === f(b)`.

```dotty
import spire.math.Rational
import spire.syntax.all.*

Rational(2, 4) === Rational(3, 6) // true
Rational(2, 6) =!= Rational(3, 6) // true
```

## Order

Общее сравнение в Spire поддерживается классом типов `Order[A]`. 
В отличие от других классов типов упорядочивания (например, `scala.math.Ordering`), 
этот специализируется на том, чтобы избежать упаковки `Order[A]`. 
Расширение `Eq[A]` можно реализовать с помощью одного метода `compare`, 
хотя `Order[A]` обеспечивает все следующее:

- `eqv` (`a === b`)
- `neqv` (`a =!= b`)
- `compare` (`a compare b`): меньше (`-1`), равно (`0`) или больше (`1`)
- `gt` (`a > b`): больше
- `gteqv` (`a >= b`): больше или равно
- `lt` (`a < b`): меньше
- `lteqv` (`a <= b`): меньше или равно
- `min` (`a min b`): наименьший элемент
- `max` (`a max b`): наибольший элемент

Экземпляры `Order[A]` должны соблюдать следующие свойства:

- если `a <= b` и `b <= a`, тогда `a === b` (_антисимметрия_)
- если `a <= b` и `b <= c`, тогда `a <= c` (_транзитивность_)
- либо `a <= b` или `b <= a` (_полнота_)

Кроме того, общее упорядочивание по полям должно подчиняться следующим дополнительным законам:

- если `a <= b`, тогда `(a + c) <= (b + c)` (_O1_)
- если `zero <= a` и `zero <= b`, тогда `zero <= (a * b)` (_O2_)

(Это законы, обязательные для упорядоченных полей.)

В некоторых случаях пользователям может потребоваться использовать (или определять) общее упорядочивание, 
которое не соответствуют всем этим законам или может нарушать законы, требуемые другими структурами. 
Примером может служить лексикографический порядок комплексных чисел, который нарушает _O2_. 
В этих случаях пользователям необходимо будет осознавать риски и ограничивать их использование ситуациями, 
когда конкретный закон не требуется.

```dotty
import spire.algebra.Order
import spire.math.Rational

Order.comparison(Rational(1, 2), Rational(1, 3)) // GreaterThan
Order.gt(Rational(1, 2), Rational(1, 3))         // true
Order.gteqv(Rational(1, 2), Rational(1, 3))      // true
Order.lt(Rational(1, 2), Rational(1, 3))         // false
Order.lteqv(Rational(1, 2), Rational(1, 3))      // false
Order.min(Rational(1, 2), Rational(1, 3))        // 1/3
Order.max(Rational(1, 2), Rational(1, 3))        // 1/2
```

## Signed

Общее упорядочивание, инвариантное к сдвигу, фиксируется классом типов `Signed[A]` - 
типы, имеющие знак (отрицательный, ноль, положительный). 
В общем случае тип `A` оснащен коммутативной аддитивной операцией `+` и нулевым элементом `0` 
(см. определение коммутативных колец ниже). 
Действуют следующие законы:

- если `a <= b`, тогда `a + c <= b + c` (линейный порядок),
- `signum(x) = -1` если `x < 0`, `signum(x) = 1` если `x > 0`, иначе - `signum(x) = 0`.

Если тип `A` оснащен отрицательными элементами `-x`, то:

- `abs(x) = -x` если `x < 0`, иначе - `x`,

Вышеуказанные законы подразумевают:

- `abs(a + b) <= abs(a) + abs(b)`

```dotty
import spire.algebra.Signed.*
import spire.std.int.*

sign(10)               // Positive
sign(0)                // Zero
sign(-10)              // Negative
signum(10)             // 1
signum(0)              // 0
signum(-10)            // -1
abs(10)                // 10
abs(0)                 // 0 
abs(-10)               // 10
isSignPositive(10)     // true
isSignZero(0)          // true
isSignNegative(-10)    // true
isSignNonPositive(10)  // false
isSignNonZero(0)       // false
isSignNonNegative(-10) // false
```

## PartialOrder

Частичное упорядочивание в Spire поддерживается классом типов `PartialOrder[A]`. 
Его реализация отличается от `scala.math.PartialOrdering` двумя особенностями: 
`PartialOrder` специализирована для предотвращения упаковки, а метод `partialCompare` возвращает `Double` 
и избегает выделения экземпляра `Option[Int]`. 
`PartialOrder[A]` расширяет `Eq[A]` и может быть реализован с помощью одного метода `partialCompare`, описанного ниже. 
`PartialOrder` обеспечивает:

- `partialCompare` (`a partialCompare b`): меньше (`-1.0`), равно (`0.0`), больше (`1.0`) или невозможность сравнения (`NaN`)
- `tryCompare` (`a tryCompare b`): меньше (`Some(-1)`), равно (`Some(0)`), больше (`Some(1)`) или невозможность сравнения (`None`)
- `pmin` (`a pmin b`): нахождение наименьшего элемента, если элементы сравнимы; возвращается `Option`
- `pmax` (`a pmax b`): нахождение наибольшего элемента, если элементы сравнимы; возвращается `Option`
- `gt` (`a > b`), `gteqv` (`a >= b`), `lt` (`a < b`) и `lteqv` (`a <= b`): возвращается `false` если элементы не сравнимы
  или результат сравнения
- `eqv` (`a === b`)
- `neqv` (`a =!= b`)

Частичное упорядочивание определяется бинарным отношением `<=`, которое удовлетворяет отношениям:

- `a <= a` (_рефлексивность_)
- если `a <= b` и `b <= a`, то `a === b` (_антисимметрия_)
- если `a <= b` и `b <= c`, то `a <= c` (_транзитивность_)

Чтобы вычислить оба значения `<=` и `>=` одновременно, 
метод `partialCompare` использует число `Double` для кодирования результата обоих сравнений. 
Таблица истинности определяется следующим образом:

| `a <= b` |`a >= b` | `partialCompare(a, b)` |    соответствует     |
|:--------:|:-------:|:----------------------:|:--------------------:|
|  `true`  | `true`  |          `0`           |      `a === b`       |
| `false`  | `false` |         `NaN`          | `a` несравнима с `b` |
|  `true`  | `false` |          `-1`          |       `a < b`        |
| `false`  | `true`  |          `1`           |       `a > b`        |

Метод `tryCompare` возвращает вместо `-1.0`, `0.0`, `1.0`, `NaN` соответственно `Some(-1)`, `Some(0)`, `Some(1)` и `None`, 
что позволяет использовать метод `getOrElse` и методы более высокого порядка по стоимости выделения `Option[Int]`.

Экземпляры `PartialOrder[A]` необходимы для соблюдения вышеуказанных свойств.

Обратите внимание, что `Order[A]` расширяет `PartialOrder[A]`, 
но в педагогических целях, `Order[A]` представлен первым в этом руководстве.

---

**Ссылки:**

- [Spire home page](https://typelevel.org/spire)
- [Spire User's Guide](https://typelevel.org/spire/guide.html)
